package org.tang.hool.context;

import org.tang.hool.annotation.Autowired;
import org.tang.hool.annotation.Controller;
import org.tang.hool.annotation.Service;
import org.tang.hool.bean.BeanDefinition;
import org.tang.hool.bean.BeanDefinitionReader;
import org.tang.hool.bean.BeanFactory;
import org.tang.hool.bean.BeanWrapper;
import org.tang.hool.util.StringUtil;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Tang Jiujia
 * @since 2022/5/5
 */
public class ApplicationContext extends DefaultListableBeanFactory implements BeanFactory {

    private final String[] locations;
    private BeanDefinitionReader reader;

    /**
     * 单例bean缓存
     */
    private Map<String, Object> singleBeanCache = new ConcurrentHashMap<>();

    /**
     * 通用bean缓存
     */
    private Map<String, BeanWrapper> instantCache = new HashMap<>();

    public ApplicationContext(String... locations) {
        this.locations = locations;
        refresh();
    }

    @Override
    protected void refresh() {
        // 1. 加载配置文件，扫描包下的类，封装成BeanDefinition
        reader = new BeanDefinitionReader(this.locations);
        List<BeanDefinition> definitionList = reader.loadBeanDefinitions();
        // 2. 注册配置信息
        registerBeanDefinition(definitionList);
        // 3. 初始化非延时加载类
        initNotLazyBean();
    }

    @Override
    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        Object instant = instantBean(beanDefinition);
        if (instant == null) return null;
        BeanWrapper beanWrapper = new BeanWrapper(instant);
        instantCache.put(beanName, beanWrapper);
        autoWiredBeanField(instant);
        return instantCache.get(beanName).getWrappedInstant();
    }

    @Override
    public Object getBean(Class<?> beanClass) {
        return getBean(beanClass.getName());
    }

    public String[] getBeanDefinitionNames() {
        return this.beanDefinitionMap.keySet()
                .toArray(new String[this.beanDefinitionMap.size()]);
    }

    public int getBeanDefinitionCount() {
        return this.beanDefinitionMap.size();
    }

    public Properties getConfig() {
        return this.reader.getConfig();
    }

    /**
     * 自动注入实例标记@Autowired的属性
     *
     * @author Tang Jiujia
     * @since 2022/5/5
     */
    private void autoWiredBeanField(Object instant) {
        Class<?> cls = instant.getClass();
        if (!cls.isAnnotationPresent(Controller.class)
                && !cls.isAnnotationPresent(Service.class)) {
            return;
        }
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(Autowired.class)) continue;
            Autowired autowired = field.getAnnotation(Autowired.class);
            String autowiredBeanName = autowired.value().trim();
            if ("".equals(autowiredBeanName)) {
                autowiredBeanName = StringUtil
                        .toLowerFirstCase(field.getType().getSimpleName());
            }
            field.setAccessible(true);
            Object wrappedInstant = instantCache.get(autowiredBeanName).getWrappedInstant();
            try {
                field.set(instant, wrappedInstant);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 注册BeanDefinition
     *
     * @author Tang Jiujia
     * @since 2022/5/5
     */
    private void registerBeanDefinition(List<BeanDefinition> beanDefinitionList) {
        for (BeanDefinition beanDefinition : beanDefinitionList) {
            String factoryBeanName = beanDefinition.getFactoryBeanName();
            if (beanDefinitionMap.containsKey(factoryBeanName)) {
                throw new RuntimeException("The " + factoryBeanName + " is exists!");
            }
            beanDefinitionMap.put(factoryBeanName, beanDefinition);
        }
    }

    /**
     * 初始化非延时加载类
     *
     * @author Tang Jiujia
     * @since 2022/5/5
     */
    private void initNotLazyBean() {
        beanDefinitionMap.forEach((key, beanDefinition) -> {
            if (!beanDefinition.isLazyInit()) {
                getBean(beanDefinition.getFactoryBeanName());
            }
        });
    }

    /**
     * 根据配置类生成bean
     *
     * @author Tang Jiujia
     * @since 2022/5/5
     */
    private Object instantBean(BeanDefinition beanDefinition) {
        String beanClassName = beanDefinition.getBeanClassName();
        Object instant = null;
        if (singleBeanCache.containsKey(beanClassName)) {
            instant = singleBeanCache.get(beanClassName);
        } else {
            try {
                Class<?> cls = Class.forName(beanClassName);
                instant = cls.newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return instant;
    }
}
